package ws

import (
	"encoding/json"
	"fmt"
	"github.com/gogf/gf/container/gmap"
	"github.com/gogf/gf/database/gdb"
	"github.com/gogf/gf/encoding/gjson"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gconv"
	"github.com/gogf/gf/util/guid"
	px_train_people "live/app/model/px_train_people"
	"live/libaray/tools"
	"live/libaray/xredis"
)

var (
	BoxUser               gmap.IntAnyMap                          // 装用户连接的盒子
	BoxRoom               map[int]*gmap.IntAnyMap                 // 装房间的盒子
	ServerId              = guid.S()                              // 当前服务器标志,订阅消息广播客户端上线时(携带此ID),非本机的连接强制下线
	roomOnlineCountPrefix = g.Cfg().GetString("room.OnlineCount") // 房间在线人数统计 key前缀
)

func init() {
	BoxRoom = make(map[int]*gmap.IntAnyMap)
}

func GetRoomOnlineKey(roomId int) string {
	return fmt.Sprintf("px:%s%d", roomOnlineCountPrefix, roomId)
}

// 系统广播消息结构体
type SysBroadcast struct {
	ServerId string      // 服务器ID
	Cmd      string      // 命令
	Data     interface{} // 命令参数
}

// 执行广播方法
func RunSysBroad(c *SysBroadcast) {
	handler, has := getHandlersSysBroadcast(c.Cmd)
	if !has {
		tools.MessageLog("ws广播路由不存在," +c.Cmd)
		g.Log().Warningf("ws广播路由不存在,%s", c.Cmd)
	} else {
		tools.MessageLog("执行广播" +gconv.String(c.Data))
		handler(c)
	}
}

// 推送到消息队列
func (c *SysBroadcast) Publish() {
	b, err := json.Marshal(c)
	if err != nil {
		g.Log().Error(err)
	}
	publish := redisCli.Publish(queueSysBroadcast, b)
	if publish.Err() != nil {
		g.Log().Error(publish.Err())
	}
}

// 消息推送 有房间roomId 推送消息到房间  有userId推送给个人
type MessagePush struct {
	UserId  int `json:"user_id"`
	RoomId  int	`json:"room_id"`
	MsgData MsgData `json:"msg_data"`// 推送给客户端的数据
}

// 执行消息方法
func RunMessagePush(c *MessagePush) {
	jsonStr, _ := gjson.New(c).ToJsonString()
	tools.MessageLog("消息内容：" + jsonStr)
	seqId := tools.GetSnowflakeId()
	msg := &JsonData{
		SeqId: seqId,
		Code:  0,
		Msg:   "",
		Data:  c.MsgData,
	}
	//tools.MessageLog("有没有MsgData：" + gconv.String(c.MsgData))
	//tools.MessageLog("有没有roomId：" + gconv.String(c.RoomId))
	if c.RoomId > 0 {
		// 推房间 弹幕,在线人数等房间里的即时消息
		msg.Cmd = "room"
		room := BoxRoom[c.RoomId]
		if room != nil {
			room.Iterator(func(k int, cli interface{}) bool {
				tools.MessageLog("消息推送：" + gconv.String(msg))
				err := cli.(*Client).SendJson(msg)
				if err != nil {
					tools.MessageLog("消息推送失败：" + err.Error())
				}
				tools.MessageLog("发送成功111：" + gconv.String(msg))
				return true
			})
		}else {
			tools.MessageLog("获取房间失败：" + gconv.String(c.RoomId))
		}
	} else {
		// 推用户 个人消息列表,即时通讯等消息
		msg.Cmd = "my"
		cli := BoxUser.Get(c.UserId)
		if cli != nil {
			err := cli.(*Client).SendJson(msg)
			if err != nil {
				tools.MessageLog("消息推送个人：" + err.Error())
			}
		}else{
			tools.MessageLog("消息推送个人：" + gconv.String(msg))
		}
	}
}

// 通过消息队列去执行
func (c *MessagePush) Publish() {
	b, err := json.Marshal(c)
	if err != nil {
		g.Log().Error(err)
	}
	publish := redisCli.Publish(queueMsg, b)
	if publish.Err() != nil {
		g.Log().Error(publish.Err())
	}
}

// 处理数据 被动接受
func OnMessage(cli *Client, message []byte) {
	if len(message) == 0 {
		message = []byte("{}")
	}
	request := new(Request)
	request.Response = new(JsonData)
	out := request.Response
	request.Data = message
	tools.Dump(message ,"==============message=============")
	err := json.Unmarshal(message, &request.Base)
	if err != nil {
		out.LogContent = fmt.Sprintf("[websocket]处理数据 json Unmarshal:%s", err.Error())
		g.Log().Error(out.LogContent)
		out.Msg = "请求格式不正确"
		_ = cli.Error(out)
		return
	}
	out.Cmd = request.Base.Cmd
	// 获取注册的路由
	if handler, ok := getHandlersMsg(out.Cmd); ok {
		g.TryCatch(func() {
			handler(cli, request)
		}, func(exception interface{}) {
			if exception != nil {
				// ws 业务处理 异常捕获
				out.Msg = "内部错误"
				out.LogContent = gconv.String(exception)
				out.LogTitle = "内部错误"
				_ = cli.Error(out)
			}
		})
	} else {
		out.LogTitle = fmt.Sprintf("[websocket]%s", out.Cmd)
		out.LogContent = "[websocket]处理数据 路由不存在"
		out.Msg = "路由不存在"
		_ = cli.Error(out)
	}
}

// 连接打开
func OnOpen(cli *Client) {
	// 下线当前服务器旧连接
	client := BoxUser.Get(cli.User.Id)
	if client != nil {
		OnClose(client.(*Client))
	}
	// 通知其他服务器 下线旧连接
	obj := &SysBroadcast{
		ServerId: ServerId,
		Cmd:      "online",
		Data:     cli.User.Id,
	}
	obj.Publish()
	// 保存新连接
	BoxUser.Set(cli.User.Id, cli)
}

// 连接关闭
func OnClose(cli *Client) {
	_ = cli.Socket.Close()
	BoxUser.Remove(cli.User.Id)
	ExitRoom(cli)
}

// 用户上线广播
func OnSysBroadOnline(req *SysBroadcast) {
	// 其他服务器下线连接
	if BoxUser.Contains(gconv.Int(req.Data)) && req.ServerId != ServerId {
		OnClose(BoxUser.Get(gconv.Int(req.Data)).(*Client))
		g.Log().Debugf("旧连接下线,uid:%v", req.Data)
	}
}

// 退出房间
func ExitRoom(cli *Client) {
	if cli.RoomId != 0 {
		room := BoxRoom[cli.RoomId]
		if room == nil {
			return
		}
		room.Remove(cli.User.Id)
		if room.Size() == 0 {
			delete(BoxRoom, cli.RoomId)
		}
		exit :=xredis.DB().Zrem("live_" + gconv.String(cli.RoomId),cli.RoomId,cli.User.Id)
		count := xredis.DB().Zcard("live_" + gconv.String(cli.RoomId))

		//离开用户 更改status为1
		_,err := px_train_people.Model.Data(gdb.Map{"status":gconv.String(1)}).Where("user_id",gconv.String(cli.User.Id)).Where("train_id",gconv.String(cli.RoomId)).Update()
		//_, err := g.DB().Table("px_train_people").Data(gdb.Map{"status": gconv.String(1)}).Where("user_id", gconv.String(cli.User.Id)).Where("train_id", gconv.String(cli.RoomId)).Update()
		if err != nil {
			g.Log().Debug("用户离开直播间错误", err)
		}
		if exit == 1{
				if count > 0 {
					// 广播房间消息
					msg := &MessagePush{
						UserId: 0,
						RoomId: cli.RoomId,
						MsgData: MsgData{
							Type: 1,
							Data: RoomInfo{
								Count: count,
								Text:  cli.User.Name + ",离开了房间",
							},
						},
					}

					msg.Publish()
				}
			}
	}
}

// 加入房间
func JoinRoom(cli *Client, roomId int) {

	if cli.RoomId == 0 {
		if cli.RoomId == roomId || cli.RoomId == 0 {
			// 已在此房间 或 房间号为0
			return
		} else {
			// 从其他房间退出
			ExitRoom(cli)
		}
	}

	// 加入房间
	room := BoxRoom[roomId]
	if room != nil {
		room.Set(cli.User.Id, cli)
	} else {
		room = gmap.NewIntAnyMap()
		room.Set(cli.User.Id, cli)
		BoxRoom[roomId] = room
	}
	add  :=xredis.DB().Zadd("live_" + gconv.String(cli.RoomId),cli.RoomId,cli.User.Id)
	count := xredis.DB().Zcard("live_"+gconv.String(cli.RoomId))
	//添加用户
	r,_ :=px_train_people.Model.Where(gdb.Map{"user_id":gconv.String(cli.User.Id),"train_id":gconv.String(cli.RoomId)}).One()
	if r != nil{
		px_train_people.Model.Data(gdb.Map{"status":gconv.String(0)}).Where("user_id",gconv.String(cli.User.Id)).Where("train_id",gconv.String(cli.RoomId)).Update()
	}else{
		px_train_people.Model.Data(gdb.Map{"user_id":gconv.String(cli.User.Id),"train_id":gconv.String(cli.RoomId)}).Insert()
	}
	cli.RoomId = roomId
	// 广播房间消息
		if add == 1{
			msg := &MessagePush{
				RoomId: cli.RoomId,
				MsgData: MsgData{
					Type: 1,
					Data: RoomInfo{
						UserId: cli.User.Id,
						Count: count,
						Text:  cli.User.Name + ",来了",
					},
				},
			}
			msg.Publish()
		}else{
			msg := &MessagePush{
				RoomId: cli.RoomId,
				MsgData: MsgData{
					Type: 1,
					Data: RoomInfo{
						UserId: cli.User.Id,
						Count: count,
						Text:  cli.User.Name + ",来了",
					},
				},
			}

			msg.Publish()
		}



}
